﻿using System.Reflection;
using System.Text;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using SqlSugar;
using Volo.Abp.Data;
using Volo.Abp.Domain;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
using Volo.Abp.MultiTenancy.ConfigurationStore;
using Yi.Framework.SqlSugarCore.Abstractions;
using Yi.Framework.SqlSugarCore.Repositories;
using Yi.Framework.SqlSugarCore.Uow;

namespace Yi.Framework.SqlSugarCore
{
    [DependsOn(typeof(AbpDddDomainModule))]
    public class YiFrameworkSqlSugarCoreModule : AbpModule
    {
        public override Task ConfigureServicesAsync(ServiceConfigurationContext context)
        {
            var service = context.Services;
            var configuration = service.GetConfiguration();
            var section = configuration.GetSection("DbConnOptions");
            Configure<DbConnOptions>(section);
            var dbConnOptions = new DbConnOptions();
            section.Bind(dbConnOptions);

            //很多人遗漏了这一点，不同的数据库，对于主键的使用规约不一样，需要根据数据库进行判断
            SequentialGuidType guidType;
            switch (dbConnOptions.DbType)
            {
                case DbType.MySql:
                case DbType.PostgreSQL:
                    guidType= SequentialGuidType.SequentialAsString;
                    break;
                case DbType.SqlServer:
                    guidType = SequentialGuidType.SequentialAtEnd;
                    break;
                case DbType.Oracle:
                    guidType = SequentialGuidType.SequentialAsBinary;
                    break;
                default:
                    guidType = SequentialGuidType.SequentialAtEnd;
                    break;
            }
            Configure<AbpSequentialGuidGeneratorOptions>(options =>
            {
                options.DefaultSequentialGuidType = guidType;
            });
            
            service.TryAddTransient<ISqlSugarDbContext, SqlSugarDbContextFactory>();

            //不开放sqlsugarClient
            //service.AddTransient<ISqlSugarClient>(x => x.GetRequiredService<ISqlsugarDbContext>().SqlSugarClient);


            service.AddTransient(typeof(IRepository<>), typeof(SqlSugarRepository<>));
            service.AddTransient(typeof(IRepository<,>), typeof(SqlSugarRepository<,>));
            service.AddTransient(typeof(ISqlSugarRepository<>), typeof(SqlSugarRepository<>));
            service.AddTransient(typeof(ISqlSugarRepository<,>), typeof(SqlSugarRepository<,>));

            service.AddTransient(typeof(ISugarDbContextProvider<>), typeof(UnitOfWorkSqlsugarDbContextProvider<>));
            //替换Sqlsugar默认序列化器，用来解决.Select()不支持嵌套对象/匿名对象的非公有访问器 值无法绑定,如Id属性
            context.Services.AddSingleton<ISerializeService, SqlSugarNonPublicSerializer>();

            var dbConfig = section.Get<DbConnOptions>();
            //将默认db传递给abp连接字符串模块
            Configure<AbpDbConnectionOptions>(x => { x.ConnectionStrings.Default = dbConfig.Url; });
            //配置abp默认租户，对接abp模块
            Configure<AbpDefaultTenantStoreOptions>(x => {
                var tenantList = x.Tenants.ToList();
                foreach(var tenant in tenantList)
                {
                    tenant.NormalizedName = tenant.Name.Contains("@") ? 
                        tenant.Name.Substring(0, tenant.Name.LastIndexOf("@")) : 
                        tenant.Name;
                }
                tenantList.Insert(0, new TenantConfiguration
                {
                    Id = Guid.Empty,
                    Name = $"{ConnectionStrings.DefaultConnectionStringName}",
                    NormalizedName = ConnectionStrings.DefaultConnectionStringName,
                    ConnectionStrings = new ConnectionStrings() { { ConnectionStrings.DefaultConnectionStringName, dbConfig.Url } },
                    IsActive = true
                });
                x.Tenants = tenantList.ToArray();
            });
             context.Services.AddYiDbContext<DefaultSqlSugarDbContext>();
            return Task.CompletedTask;
        }


        public override async Task OnPreApplicationInitializationAsync(ApplicationInitializationContext context)
        {
            //进行CodeFirst
            var service = context.ServiceProvider;
            var options = service.GetRequiredService<IOptions<DbConnOptions>>().Value;

            var logger = service.GetRequiredService<ILogger<YiFrameworkSqlSugarCoreModule>>();


            StringBuilder sb = new StringBuilder();
            sb.AppendLine();
            sb.AppendLine("==========Yi-SQL配置:==========");
            sb.AppendLine($"数据库连接字符串：{options.Url}");
            sb.AppendLine($"数据库类型：{options.DbType.ToString()}");
            sb.AppendLine($"是否开启种子数据：{options.EnabledDbSeed}");
            sb.AppendLine($"是否开启CodeFirst：{options.EnabledCodeFirst}");
            sb.AppendLine($"是否开启Saas多租户：{options.EnabledSaasMultiTenancy}");
            sb.AppendLine("===============================");


            logger.LogInformation(sb.ToString());

            if (options.EnabledCodeFirst)
            {
                CodeFirst(service);
            }

            if (options.EnabledDbSeed)
            {
                await DataSeedAsync(service);
            }
        }

        private void CodeFirst(IServiceProvider service)
        {
            var moduleContainer = service.GetRequiredService<IModuleContainer>();
            var db = service.GetRequiredService<ISqlSugarDbContext>().SqlSugarClient;

            //尝试创建数据库
            db.DbMaintenance.CreateDatabase();

            List<Type> types = new List<Type>();
            foreach (var module in moduleContainer.Modules)
            {
                types.AddRange(module.Assembly.GetTypes()
                    .Where(x => x.GetCustomAttribute<IgnoreCodeFirstAttribute>() == null)
                    .Where(x => x.GetCustomAttribute<SugarTable>() != null)
                    .Where(x => x.GetCustomAttribute<SplitTableAttribute>() is null));
            }

            if (types.Count > 0)
            {
                db.CopyNew().CodeFirst.InitTables(types.ToArray());
            }
        }

        private async Task DataSeedAsync(IServiceProvider service)
        {
            var dataSeeder = service.GetRequiredService<IDataSeeder>();
            await dataSeeder.SeedAsync();
        }
    }
}